package com.culture.bootdemo.utils;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author: wenjl
 * @Date: 2021/5/28 20:10
 * @Description: redis 操作类
 * <p>
 * <p>
 * redis 命令参考：http://doc.redisfans.com/
 */
@Component
@Slf4j
public class RedisDao {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * hash结构，返回map
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hmGet(String key) {
        if (StringUtils.isBlank(key)) {
            return new HashMap<>();
        }
        return redisTemplate.opsForHash().entries(key);
    }


    /**
     * hash结构，返回map
     * 自己拼装的map ，不推荐，可用上面 hmGet()方法
     *
     * @param key
     * @return 弃用
     */
    @Deprecated
    public Map<String, Object> hGetAll(String key) {
        if (StringUtils.isBlank(key)) {
            return new HashMap<>();
        }
        Set<Object> fields = redisTemplate.opsForHash().keys(key);
        if (CollectionUtils.isEmpty(fields)) {
            return new HashMap<>();
        }
        Map<String, Object> resultMap = Maps.newHashMapWithExpectedSize(fields.size());
        fields.forEach(data -> resultMap.put(data.toString(), redisTemplate.opsForHash().get(key, data)));
        return resultMap;
    }

    /**
     * 插入值，成功则返回true并设置过期时间，失败则表示已存在，返回false
     * 多用于线程锁之类
     *
     * @param key
     * @param value
     * @param timeSeconds
     * @return
     */
    public boolean setNX(String key, Object value, long timeSeconds) {
        Boolean b = redisTemplate.opsForValue().setIfAbsent(key, value);
        if (b) {
            redisTemplate.expire(key, timeSeconds, TimeUnit.SECONDS);
            return true;
        }
        return false;
    }

    // ===============================bitmap 操作：============================

    /**
     * bitmap结构
     *
     * @param key
     * @param offSet
     * @param b
     * @return
     */
    public Boolean setBit(String key, long offSet, boolean b) {
        return redisTemplate.opsForValue().setBit(key, offSet, b);
    }

    /**
     * bitmap结构
     *
     * @param key
     * @param offSet
     * @return
     */
    public Boolean getBit(String key, long offSet) {
        return redisTemplate.opsForValue().getBit(key, offSet);
    }

    /**
     * bitmap结构
     * 查询某个key的数量
     * 时间复杂度：O(N)
     *
     * @param key
     * @return
     */
    public Long bitCount(String key) {
        return redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes()));
    }

    /**
     * 统计key字段value为1的总数,从start开始到end结束
     *
     * @param key   字段
     * @param start 起始
     * @param end   结束
     * @return 总数
     */
    public Long bitCount(String key, Long start, Long end) {
        return redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitCount(key.getBytes(), start, end));
    }

    /**
     * 取多个key并集并计算总数
     *
     * @param key key
     * @return 总数
     */
    public Long OpOrCount(String... key) {
        byte[][] keys = new byte[key.length][];
        for (int i = 0; i < key.length; i++) {
            keys[i] = key[i].getBytes();
        }
        redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitOp(RedisStringCommands.BitOperation.OR, (key[0] + "To" + key[key.length - 1]).getBytes(), keys));
        redisTemplate.expire(key[0] + "To" + key[key.length - 1], 10, TimeUnit.SECONDS);
        return bitCount(key[0] + "To" + key[key.length - 1]);
    }

    /**
     * 取多个key的交集并计算总数
     *
     * @param key key
     * @return 总数
     */
    public Long OpAndCount(String... key) {
        byte[][] keys = new byte[key.length][];
        for (int i = 0; i < key.length; i++) {
            keys[i] = key[i].getBytes();
        }
        redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitOp(RedisStringCommands.BitOperation.AND, (key[0] + "To" + key[key.length - 1]).getBytes(), keys));
        redisTemplate.expire(key[0] + "To" + key[key.length - 1], 10, TimeUnit.SECONDS);
        return bitCount(key[0] + "To" + key[key.length - 1]);
    }

    /**
     * 取多个key的补集并计算总数
     *
     * @param key key
     * @return 总数
     */
    public Long OpXorCount(String... key) {
        byte[][] keys = new byte[key.length][];
        for (int i = 0; i < key.length; i++) {
            keys[i] = key[i].getBytes();
        }
        redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitOp(RedisStringCommands.BitOperation.XOR, (key[0] + "To" + key[key.length - 1]).getBytes(), keys));
        redisTemplate.expire(key[0] + "To" + key[key.length - 1], 10, TimeUnit.SECONDS);
        return bitCount(key[0] + "To" + key[key.length - 1]);
    }

    /**
     * 取多个key的否集并计算总数
     *
     * @param key key
     * @return 总数
     */
    public Long OpNotCount(String... key) {
        byte[][] keys = new byte[key.length][];
        for (int i = 0; i < key.length; i++) {
            keys[i] = key[i].getBytes();
        }
        redisTemplate.execute((RedisCallback<Long>) conn -> conn.bitOp(RedisStringCommands.BitOperation.NOT, (key[0] + "To" + key[key.length - 1]).getBytes(), keys));
        redisTemplate.expire(key[0] + "To" + key[key.length - 1], 10, TimeUnit.SECONDS);
        return bitCount(key[0] + "To" + key[key.length - 1]);
    }

}
